// ValidationAspects
// Written by Mike Saunders
// Released under Microsoft Public License (Ms-PL)
using System;
using System.Linq;
using System.Reflection;
using NUnit.Framework;
using ValidationAspects;
using ValidationAspects.Exceptions;
using ValidationAspects.Sdk.Interception;

namespace TestValidationAspects.Sdk.Interception
{
	[TestFixture]
	public class TestInterceptValidateMethod
	{
		[SetUp]
		public void SetUp()
		{
			TestHelpers.ResetFramework();
		}

		[TestFixtureTearDown]
		public void TestFixtureTearDown()
		{
			TestHelpers.ResetFramework();
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void Test_InterceptValidateMethod_throws_for_null_PropertyInfo_param()
		{
			InterceptValidate.Method(null, new object(), new object[] {1});
		}

		[Test]
		public void Test_InterceptValidateMethod_throws_caught_ValidationException_and_populates_context()
		{
			MethodInfo method = typeof(Dummy).GetMethod("Method");
			MockValidatorFactory<string> stringValidator = new MockValidatorFactory<string>();
			MockValidatorFactory<object> objectValidator = new MockValidatorFactory<object>();

			method.ReplaceValidation(new[] {objectValidator});
			method.GetParameters()[0].ReplaceValidation(new[] {stringValidator});

			stringValidator.Fail = true;
			objectValidator.Fail = true;

			object instance = new Dummy();
			object[] values = new object[] {"text"};

			try
			{
				InterceptValidate.Method(method, instance, values);
			}
			catch (ValidationContextException e)
			{
				e.Context.TestValues(instance, typeof(Dummy), null, method, null, values);

				ValidationException[] exceptions = e.Exceptions.ToArray();
				Assert.AreEqual(2, exceptions.Count());

				ValidationException exception = exceptions[0];
				exception.Context.TestValues(instance, typeof(Dummy), null, method, null, values);
				Assert.AreEqual("failed", exception.Message);

				exception = exceptions[1];
				exception.Context.TestValues(instance, typeof(Dummy), null, method, method.GetParameters()[0], values);
				Assert.AreEqual("failed", exception.Message);
				return;
			}
			Assert.Fail("ValidationContextException not thrown");
		}

		[Test]
		public void Test_InterceptValidateMethod_for_static_method_throws_caught_ValidationException_and_populates_context()
		{
			MethodInfo method = typeof(Dummy).GetMethod("StaticMethod", BindingFlags.Public | BindingFlags.Static);
			MockValidatorFactory<string> stringValidator = new MockValidatorFactory<string>();

			method.GetParameters()[0].ReplaceValidation(new[] {stringValidator});

			stringValidator.Fail = true;

			object[] values = new object[] {"text"};

			try
			{
				InterceptValidate.Method(method, null, values);
			}
			catch (ValidationContextException e)
			{
				e.Context.TestValues(null, typeof(Dummy), null, method, null, values);

				ValidationException[] exceptions = e.Exceptions.ToArray();
				Assert.AreEqual(1, exceptions.Count());

				ValidationException exception = exceptions[0];
				exception.Context.TestValues(null, typeof(Dummy), null, method, method.GetParameters()[0], values);
				Assert.AreEqual("failed", exception.Message);
				return;
			}
			Assert.Fail("ValidationContextException not thrown");
		}

		[Test]
		public void Test_InterceptValidateMethod_for_no_registered_validator()
		{
			InterceptValidate.Method(typeof(Dummy).GetMethod("Method"), new Dummy(), new object[] {"text"});
		}

		[Test]
		public void Test_InterceptValidateMethod()
		{
			MethodInfo method = typeof(Dummy).GetMethod("Method");

			MockValidatorFactory<string> stringValidator = new MockValidatorFactory<string>();
			ParameterInfo stringParameter = method.GetParameters()[0];
			stringParameter.ReplaceValidation(new[] {stringValidator});

			MockValidatorFactory<object> objectValidator = new MockValidatorFactory<object>();
			method.ReplaceValidation(new[] {objectValidator});

			object instance = new object();
			object[] arguments = new object[] {"text"};
			InterceptValidate.Method(method, instance, arguments);

			Assert.AreEqual("text", stringValidator.Value);
			stringValidator.Context.TestValues(instance, typeof(Dummy), null, method, stringParameter, arguments);

			Assert.AreSame(instance, objectValidator.Value);
			objectValidator.Context.TestValues(instance, typeof(Dummy), null, method, null, arguments);
		}

		#region Mocks
		public class Dummy
		{
			public void Method(string s) {}
			public static void StaticMethod(string s) {}
		}
		#endregion
	}
}